import {Wall} from '../pos.js'
import {Grid} from '../pos.js'

export class Maze {
  constructor(grid_num) {
    this.grid_num = grid_num
    this.H = 0
    this.V = 1
    this.free = 0
    this.busy = 1
    this.wall = new Array()
    this.tag = new Array()
    this.cluster = new Array()
    this.cluster_index = -1
    for (let i = 0; i < grid_num; i++) {
      let row = new Array()
      for (let j = 0; j < grid_num; j++) {
        if (i != 0) {
          this.wall.push(new Wall(i, j, this.V))
        }
        if (j != 0) {
          this.wall.push(new Wall(i, j, this.H))
        }
        row.push(this.free)
      }
      this.tag.push(row)
    }
  }

  _get_random(n) {
    return Math.floor(Math.random() * n)
  }

  _get_neighbor() {
    let wall_index = this._get_random(this.wall.length)
    let wall = this.wall[wall_index]
    this.wall.splice(wall_index, 1)
    if (wall.d == this.H) {
      return {g1: new Grid(wall.x, wall.y), g2: new Grid(wall.x, wall.y - 1), wall: wall}
    } else {
      return {g1: new Grid(wall.x, wall.y), g2: new Grid(wall.x - 1, wall.y), wall: wall}
    }
  }

  _is_start_end(g) {
    return (g.x == 0 && g.y == 0) || (g.x == this.grid_num - 1 && g.y == this.grid_num - 1)
  }

  _update_busy_free(g1, g2) {
    let index = -1
    for (let i = 0; i < this.cluster.length; i++) {
      if (this.cluster[i].has(g1.toString())) {
        index = i
      }
    }
    this.cluster[index].add(g2.toString())
  }

  _update_busy_busy(g1, g2) {
    let index1 = -1
    let index2 = -1
    for (let i = 0; i < this.cluster.length; i++) {
      if (this.cluster[i].has(g1.toString())) {
        index1 = i
      }
      if (this.cluster[i].has(g2.toString())) {
        index2 = i
      }
      if (index1 != -1 && index2 != -1) {
        break
      }
    }
    if (index1 == index2) {
      return
    }
    let min_index = index1 < index2 ? index1 : index2
    let max_index = index1 < index2 ? index2 : index1
    let cluster1 = this.cluster[min_index]
    let cluster2 = this.cluster[max_index]
    for (let s of cluster2) {
      cluster1.add(s)
    }
    this.cluster.splice(max_index, 1)
  }

  _update_free_free(g1, g2) {
    let current_class = new Set()
    current_class.add(g1.toString())
    current_class.add(g2.toString())
    if (this._is_start_end(g1) || this._is_start_end(g2)) {
      this.cluster.unshift(current_class)
    } else {
      this.cluster.push(current_class)
    }
  }

  _check_success(cluster) {
    return cluster.has("0_0") && cluster.has((this.grid_num - 1) + "_" + (this.grid_num - 1))
  }

  create() {
    let wall_removed = new Array()
    while (true) {
      let neighbors = this._get_neighbor()
      let g1 = neighbors.g1
      let g2 = neighbors.g2
      wall_removed.push(neighbors.wall.toString())
      if ((this.tag[g1.x][g1.y] == this.busy) &&
        (this.tag[g2.x][g2.y] == this.busy)) {
        this._update_busy_busy(g1, g2)
        if (this._check_success(this.cluster[0])) {
          break
        }
      }
      if ((this.tag[g1.x][g1.y] == this.busy) &&
        (this.tag[g2.x][g2.y] == this.free)) {
        this._update_busy_free(g1, g2)
        if (this._check_success(this.cluster[0])) {
          break
        }
        this.tag[g2.x][g2.y] = this.busy
      }
      if ((this.tag[g1.x][g1.y] == this.free) &&
        (this.tag[g2.x][g2.y] == this.busy)) {
        this._update_busy_free(g2, g1)
        if (this._check_success(this.cluster[0])) {
          break
        }
        this.tag[g1.x][g1.y] = this.busy
      }
      if ((this.tag[g1.x][g1.y] == this.free) &&
        (this.tag[g2.x][g2.y] == this.free)) {
        this._update_free_free(g1, g2)
        this.tag[g1.x][g1.y] = this.busy
        this.tag[g2.x][g2.y] = this.busy
      }
    }
    return wall_removed
  }
}